package com.trg.flex.frm.validation
{
	import com.trg.flex.FlexUtils;
	import com.trg.flex.frm.FormInput;
	import com.trg.flex.frm.FormUtils;
	
	import flash.events.Event;
	
	import mx.events.PropertyChangeEvent;
	import mx.events.PropertyChangeEventKind;
	import mx.validators.ValidationResult;
	
	public class InputValidationHelper
	{
		public var supressErrorDisplay:Boolean = false;
		
		private var input:FormInput;
		private var valueProp:String;
		private var triggerEvents:Array;
		
		public function InputValidationHelper(input:FormInput, valueProperty:String, ...triggerEvents:Array)
		{
			this.input = input;
			this.valueProp = valueProperty;
			this.triggerEvents = triggerEvents;
		}
		
		private var _type:Class;
		private var metaData:Object;
		private var propMetaData:Object = {};
		private var mergedMetaData:Object;
		private var _valid:Boolean;
		private var invalidRequired:Boolean;
		private var validated:Boolean = false;
		
		private var listenForValidation:Boolean = false;
		private function setListenForValidation(on:Boolean):void {
			var event:String;
			if (on && !listenForValidation) {
				for each (event in triggerEvents) {
					input.addEventListener(event, validate);
				}
			} else if (!on && listenForValidation) {
				for each (event in triggerEvents) {
					input.removeEventListener(event, validate);
				}
			}
		}
		
		private function validate(event:Event = null):void {
			var oldValid:Boolean = _valid;
			
			var result:ValidationResult = FormUtils.validate(_type, mergedMetaData, input[valueProp]);
			_valid = !result.isError;
			invalidRequired = result.isError && result.errorCode == "requiredField";
			validated = true;
			
			if (event != null) {
				if (!supressErrorDisplay || result.errorMessage == "") {
					Object(input).errorString = result.errorMessage;
				}
				if (oldValid != _valid) {
					input.dispatchEvent(new PropertyChangeEvent("validChanged", false, false, PropertyChangeEventKind.UPDATE, "valid", oldValid, _valid, input));
				}
			}
		}
		
		public function get valid():Boolean {
			if (!validated) {
				validate();
			}
			return _valid;
		}
		
		public function isInvalidRequired():Boolean {
			if (!validated) {
				validate();
			}
			return invalidRequired;
		}
		
		public function get type():Class {
			return _type;
		}
		
		public function set type(value:Class):void {
			_type = value;
			validated = false;
			if (FormUtils.needsValidation(_type, mergedMetaData)) {
				setListenForValidation(true);
			} else {
				setListenForValidation(false);
			}
		}
		
		public function setMetaData(metaData:Object):void {
			this.metaData = metaData;
			mergeMetaData();
			validated = false;
		}
		
		private function mergeMetaData():void {
			if (metaData == null) {
				mergedMetaData = propMetaData;
			} else {
				mergedMetaData = FlexUtils.shallowCopy(metaData);
				FlexUtils.shallowCopy(propMetaData, mergedMetaData);
			}
			if (FormUtils.needsValidation(_type, mergedMetaData)) {
				setListenForValidation(true);
			} else {
				setListenForValidation(false);
			}
		}
		
		public function setMetaDataProp(prop:String, value:* = undefined):void {
			if (value === undefined) {
				delete propMetaData[prop];
			} else {
				propMetaData[prop] = value;
			}
			mergeMetaData();
			validated = false;
		}
		
		public function setNotValidated():void {
			validated = false;
		}
		
		public function getMergedMetaData():Object {
			return mergedMetaData;
		}
	}
}